package com.atdel.demo32.util;

import com.atdel.demo32.anno.Table;
import com.atdel.demo32.entity.UserInfo;
import com.atdel.demo32.anno.Column;

import java.lang.reflect.Field;

/**
 * @BelongsProject: java_study
 * @BelongsPackage: com.atdel.demo32.util
 * @Author: Del
 * @CreateTime: 2025-03-30  17:25
 * @Description:
 * 实现自动生成 sql语句
 * 输出结果
 *     1）无注解的情况：select id,name from user
 *     2）在字段上有注解：select id,username form user
 *     3）类上有注解：select id,name from t_user
 *     4）字段和类上都有注解：select id,username from t_user
 * @Version: 1.0
 */
public class SqlGenerate {
    public static void main(String[] args) {
        String sql = generateSelect(UserInfo.class);
        System.out.println(sql);
    }

    /**
     * @author: Del
     * @date: 2025/3/30 17:46
     * 根据给定的类生成一个SQL SELECT语句。
     * 该方法通过反射获取类的字段信息，并构建一个基本的SELECT查询语句。
     *
     * @param clz 要生成SELECT语句的类。该类的字段信息将被用于构建SQL语句。
     * @return 返回构建好的SQL SELECT语句字符串。
     */
    public static String generateSelect(Class<?> clz){
        // 初始化SQL语句的开头
        StringBuilder sb = new StringBuilder("select ");

        // 获取类的所有声明的字段
        Field[] declaredFields = clz.getDeclaredFields();

        // 调用方法获取转换后的字段字符串
        String transitionField = getTransitionField(declaredFields);

        // 调用方法获取转换后的表名字符串
        String transitionTable = getTransitionTable(clz);

        // 拼接SQL语句并返回
        return sb.append(transitionField).append(" from ").append(transitionTable).toString();
    }

    /**
     * @author: Del
     * @date: 2025/3/30 17:47
     * @param clz 要获取转换表的类
     * @return String 返回表名
     * @description: 根据类获取转换表的名称如果类上有Table注解，则返回注解中指定的表名；
     * 否则，基于类的简单名称生成并返回表名
     */
    private static String getTransitionTable(Class<?> clz){
        // 检查类是否有Table注解
        if (clz.isAnnotationPresent(Table.class)) {
            // 获取类上的Table注解
            Table table = clz.getAnnotation(Table.class);
            // 返回Table注解中的表名
            return table.value();
        }
        // 如果类没有Table注解，则基于类的简单名称生成并返回表名
        return formatTableName(clz.getSimpleName());
    }



    /**
     * @author: Del
     * @date: 2025/3/30 17:47
     * @param tableName 数据库表名
     * @return 格式化后的表名
     * @description: 将驼峰命名法的表名转换为下划线命名法
     */
    private static String formatTableName(String tableName) {
        // 使用正则表达式匹配驼峰命名法中的大写字母，并在其前后添加下划线，然后将整个字符串转为小写
        return tableName.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
    }

    /**
     * @author: Del
     * @date: 2025/3/30 17:48
     * @param fields 字段数组，代表类中的所有字段
     * @return String 返回拼接后的字段名称字符串
     * @description: 该方法用于获取字段数组中所有字段的名称，并将其拼接成一个字符串
     *               主要用于生成数据库查询中的字段列表
     */
    private static String getTransitionField(Field[]  fields){
        StringBuilder sb = new StringBuilder();

        // 遍历字段数组
        for (Field field : fields) {

            // 获取当前字段对应的列名称
            String columnName = getColumnName(field);
            // 如果列名称不为空，则进行拼接
            if (columnName != null && !columnName.isEmpty()) {

                // 如果字符串构建器中有内容，则先追加逗号分隔
                if (sb.length() > 0) {
                    sb.append(",");
                }

                // 追加当前列名称
                sb.append(columnName);
            }
        }
        // 返回拼接后的字符串
        return sb.toString();
    }

    /**
     * @author: Del
     * @date: 2025/3/30 17:49
     * @param field 字段对象，用于获取字段的注解信息或字段名称
     * @return String 返回字段对应的数据库列名
     * @description: 通过字段对象获取数据库列名 如果字段上有@Column注解，则使用注解指定的列名；
     *                否则，使用字段的名称作为数据库列名
     */
    private static String getColumnName(Field field) {
        // 检查字段是否有@Column注解
        if (field.isAnnotationPresent(Column.class)) {
            // 获取字段上的@Column注解
            Column column = field.getAnnotation(Column.class);
            // 返回@Column注解指定的列名
            return column.columnName();
        }
        // 如果字段没有@Column注解，返回字段的名称作为列名
        return field.getName();
    }
}
